package cn.iocoder.yudao.module.reading.service.plan;

import cn.iocoder.yudao.module.reading.controller.app.plan.vo.PlanDailyTaskRespVO;
import cn.iocoder.yudao.module.reading.dal.dataobject.book.PictureBookDO;
import cn.iocoder.yudao.module.reading.dal.dataobject.plan.PlanBookSetDO;
import cn.iocoder.yudao.module.reading.dal.dataobject.plan.PlanDailyTaskDO;
import cn.iocoder.yudao.module.reading.dal.dataobject.plan.ReadingPlanDO;
import cn.iocoder.yudao.module.reading.dal.mysql.book.PictureBookMapper;
import cn.iocoder.yudao.module.reading.dal.mysql.plan.PlanBookSetMapper;
import cn.iocoder.yudao.module.reading.dal.mysql.plan.PlanDailyTaskMapper;
import cn.iocoder.yudao.module.reading.dal.mysql.plan.ReadingPlanMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import jakarta.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.reading.enums.ErrorCodeConstants.*;

/**
 * 计划每日任务 Service 实现类
 *
 * @author 李晨
 */
@Service
@Validated
@Slf4j
public class PlanDailyTaskServiceImpl implements PlanDailyTaskService {

    @Resource
    private PlanDailyTaskMapper planDailyTaskMapper;
    @Resource
    private ReadingPlanMapper readingPlanMapper;
    @Resource
    private PlanBookSetMapper planBookSetMapper;
    @Resource
    private PictureBookMapper pictureBookMapper;

    @Override
    public Long createTask(PlanDailyTaskDO task) {
        planDailyTaskMapper.insert(task);
        return task.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchCreateTasks(List<PlanDailyTaskDO> tasks) {
        for (PlanDailyTaskDO task : tasks) {
            planDailyTaskMapper.insert(task);
        }
    }

    @Override
    public PlanDailyTaskDO getTask(Long id) {
        PlanDailyTaskDO task = planDailyTaskMapper.selectById(id);
        if (task == null) {
            throw exception(PLAN_TASK_NOT_EXISTS);
        }
        return task;
    }

    @Override
    public List<PlanDailyTaskDO> getTasksByPlanId(Long planId, String status) {
        return planDailyTaskMapper.selectByPlanId(planId);
    }

    @Override
    public List<PlanDailyTaskDO> getTasksByPlanIdAndDate(Long planId, LocalDate taskDate) {
        return planDailyTaskMapper.selectByPlanIdAndDate(planId, taskDate);
    }

    @Override
    public List<PlanDailyTaskDO> getTasksByUserIdAndDate(Long userId, LocalDate taskDate) {
        return planDailyTaskMapper.selectByUserIdAndDate(userId, taskDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeTask(Long taskId, Integer readingTime, String notes) {
        PlanDailyTaskDO task = getTask(taskId);

        PlanDailyTaskDO updateObj = PlanDailyTaskDO.builder()
                .id(taskId)
                .isCompleted(true)
                .completedAt(LocalDateTime.now())
                .build();

        planDailyTaskMapper.updateById(updateObj);

        // 更新计划进度
        updatePlanProgress(task.getPlanId());

        log.info("任务完成: taskId={}, readingTime={}, notes={}", taskId, readingTime, notes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void skipTask(Long taskId, String reason) {
        PlanDailyTaskDO task = getTask(taskId);
        
        // 这里可以添加跳过任务的逻辑，比如记录跳过原因
        log.info("任务跳过: taskId={}, reason={}", taskId, reason);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetTask(Long taskId) {
        PlanDailyTaskDO task = getTask(taskId);
        
        PlanDailyTaskDO updateObj = PlanDailyTaskDO.builder()
                .id(taskId)
                .isCompleted(false)
                .completedAt(null)
                .build();
        
        planDailyTaskMapper.updateById(updateObj);

        // 更新计划进度
        updatePlanProgress(task.getPlanId());

        log.info("任务重置: taskId={}", taskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTasksByPlanId(Long planId) {
        planDailyTaskMapper.deleteByPlanId(planId);
        log.info("删除计划任务: planId={}", planId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateTasks(Long planId) {
        // 1. 获取计划信息
        ReadingPlanDO plan = readingPlanMapper.selectById(planId);
        if (plan == null) {
            throw exception(READING_PLAN_NOT_EXISTS);
        }

        // 2. 获取计划关联的绘本集（按sortOrder排序）
        List<PlanBookSetDO> planBookSets = planBookSetMapper.selectByPlanId(planId);
        if (planBookSets.isEmpty()) {
            log.warn("计划没有关联绘本集: planId={}", planId);
            return;
        }

        // 按sortOrder排序，确保按照用户设定的顺序生成任务
        planBookSets.sort(Comparator.comparing(PlanBookSetDO::getSortOrder));

        // 3. 按绘本集顺序获取绘本
        List<PictureBookDO> orderedBooks = new ArrayList<>();
        for (PlanBookSetDO planBookSet : planBookSets) {
            List<PictureBookDO> booksInSet = pictureBookMapper.selectByBookSetId(planBookSet.getBookSetId());
            // 可以在这里添加绘本集内部的排序逻辑，比如按照绘本的sortOrder排序
            booksInSet.sort(Comparator.comparing(PictureBookDO::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder())));
            orderedBooks.addAll(booksInSet);
        }

        // 4. 生成任务
        List<PlanDailyTaskDO> tasks = generateTasksForPlan(plan, orderedBooks);

        // 5. 批量保存任务
        batchCreateTasks(tasks);

        log.info("生成计划任务完成: planId={}, taskCount={}", planId, tasks.size());
    }

    @Override
    public List<PlanDailyTaskRespVO> convertToRespVO(List<PlanDailyTaskDO> tasks) {
        List<PlanDailyTaskRespVO> result = new ArrayList<>();
        
        for (PlanDailyTaskDO task : tasks) {
            // 获取绘本信息
            PictureBookDO book = pictureBookMapper.selectById(task.getBookId());
            
            PlanDailyTaskRespVO respVO = new PlanDailyTaskRespVO();
            respVO.setId(task.getId());
            respVO.setPlanId(task.getPlanId());
            respVO.setBookId(task.getBookId());
            respVO.setBookTitle(book != null ? book.getTitle() : "未知绘本");
            respVO.setBookCover(book != null ? book.getCover() : "");
            respVO.setTaskDate(task.getTaskDate());
            respVO.setTaskType(task.getTaskType());
            respVO.setStatus(task.getIsCompleted() ? "completed" : "pending");
            respVO.setIsReview("review".equals(task.getTaskType()));
            respVO.setCompletedTime(task.getCompletedAt() != null ? task.getCompletedAt().toString() : null);

            // 转换URL字段
            respVO.convertUrls();

            result.add(respVO);
        }

        return result;
    }

    /**
     * 为计划生成任务
     */
    private List<PlanDailyTaskDO> generateTasksForPlan(ReadingPlanDO plan, List<PictureBookDO> books) {
        List<PlanDailyTaskDO> tasks = new ArrayList<>();

        LocalDate startDate = plan.getStartDate();
        int dailyCount = plan.getDailyCount();
        List<String> reviewSchedule = plan.getReviewSchedule();

        // 1. 生成新学习任务
        List<PlanDailyTaskDO> newLearningTasks = generateNewLearningTasks(plan, books);
        tasks.addAll(newLearningTasks);

        // 2. 生成复习任务
        List<PlanDailyTaskDO> reviewTasks = generateReviewTasks(plan, newLearningTasks, reviewSchedule);
        tasks.addAll(reviewTasks);

        return tasks;
    }

    /**
     * 生成新学习任务
     */
    private List<PlanDailyTaskDO> generateNewLearningTasks(ReadingPlanDO plan, List<PictureBookDO> books) {
        List<PlanDailyTaskDO> tasks = new ArrayList<>();

        LocalDate startDate = plan.getStartDate();
        int dailyCount = plan.getDailyCount();

        int bookIndex = 0;
        int dayNumber = 1;

        while (bookIndex < books.size()) {
            LocalDate taskDate = startDate.plusDays(dayNumber - 1);

            // 为当天生成新学习任务
            for (int i = 0; i < dailyCount && bookIndex < books.size(); i++) {
                PictureBookDO book = books.get(bookIndex);

                PlanDailyTaskDO task = PlanDailyTaskDO.builder()
                        .planId(plan.getId())
                        .userId(plan.getUserId())
                        .taskDate(taskDate)
                        .dayNumber(dayNumber)
                        .taskType("new_learning")
                        .bookId(book.getId())
                        .bookSetId(book.getBookSetId())
                        .sortOrder(i + 1)
                        .isCompleted(false)
                        .build();

                tasks.add(task);
                bookIndex++;
            }

            dayNumber++;
        }

        return tasks;
    }

    /**
     * 生成复习任务
     */
    private List<PlanDailyTaskDO> generateReviewTasks(ReadingPlanDO plan,
                                                      List<PlanDailyTaskDO> newLearningTasks,
                                                      List<String> reviewSchedule) {
        List<PlanDailyTaskDO> reviewTasks = new ArrayList<>();

        // 如果复习策略包含"0"，表示不复习
        if (reviewSchedule.contains("0")) {
            return reviewTasks;
        }

        LocalDate startDate = plan.getStartDate();

        // 为每个新学习任务生成对应的复习任务
        for (PlanDailyTaskDO newTask : newLearningTasks) {
            int originalDay = newTask.getDayNumber();

            // 根据复习策略生成复习任务
            for (String reviewDayStr : reviewSchedule) {
                try {
                    int reviewDayOffset = Integer.parseInt(reviewDayStr);
                    int reviewDay = originalDay + reviewDayOffset;
                    LocalDate reviewDate = startDate.plusDays(reviewDay - 1);

                    // 获取当天已有的复习任务数量，用于排序
                    long existingReviewCount = reviewTasks.stream()
                            .filter(task -> task.getTaskDate().equals(reviewDate))
                            .count();

                    PlanDailyTaskDO reviewTask = PlanDailyTaskDO.builder()
                            .planId(plan.getId())
                            .userId(plan.getUserId())
                            .taskDate(reviewDate)
                            .dayNumber(reviewDay)
                            .taskType("review")
                            .bookId(newTask.getBookId())
                            .bookSetId(newTask.getBookSetId())
                            .sortOrder((int) (existingReviewCount + 1000)) // 复习任务排在新学习任务后面
                            .isCompleted(false)
                            .build();

                    reviewTasks.add(reviewTask);

                } catch (NumberFormatException e) {
                    log.warn("无效的复习天数: {}", reviewDayStr);
                }
            }
        }

        return reviewTasks;
    }

    @Override
    public void completeTaskByBookId(Long userId, Long planId, Long bookId) {
        // 检查是否存在最近完成的任务（防止短时间内重复调用）
        List<PlanDailyTaskDO> allTasksForBook = planDailyTaskMapper.selectTaskStatusByBookId(planId, bookId);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime fiveMinutesAgo = now.minusMinutes(5);

        // 查找最近5分钟内完成的任务
        List<PlanDailyTaskDO> recentlyCompletedTasks = allTasksForBook.stream()
                .filter(PlanDailyTaskDO::getIsCompleted)
                .filter(task -> task.getCompletedAt() != null && task.getCompletedAt().isAfter(fiveMinutesAgo))
                .collect(Collectors.toList());

        // 如果最近5分钟内已经有任务完成，可能是重复调用
        if (!recentlyCompletedTasks.isEmpty()) {
            log.warn("检测到该绘本在最近5分钟内已有任务完成，跳过重复处理: userId={}, planId={}, bookId={}",
                    userId, planId, bookId);
            return;
        }

        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 首先查找今天的未完成任务
        List<PlanDailyTaskDO> todayTasks = planDailyTaskMapper.selectTodayUncompletedTasks(planId, bookId, today);

        List<PlanDailyTaskDO> tasksToCheck = todayTasks;

        // 如果今天没有任务，查找该绘本的所有未完成任务
        if (todayTasks.isEmpty()) {
            tasksToCheck = planDailyTaskMapper.selectUncompletedTasksByBookId(planId, bookId);
            log.info("今天没有找到任务，查找该绘本的所有未完成任务，找到 {} 个任务", tasksToCheck.size());
        }

        if (tasksToCheck.isEmpty()) {
            log.warn("未找到需要完成的计划任务，用户ID: {}, 计划ID: {}, 绘本ID: {}", userId, planId, bookId);
            return;
        }

        // 改进的任务选择逻辑：
        // 1. 优先选择新学任务（按日期排序，选择最早的）
        // 2. 如果没有新学任务，选择今天应该完成的复习任务
        // 3. 如果今天没有复习任务，选择已经过期的复习任务
        PlanDailyTaskDO taskToComplete = null;

        // 首先尝试选择新学任务
        Optional<PlanDailyTaskDO> newLearningTask = tasksToCheck.stream()
                .filter(task -> "new_learning".equals(task.getTaskType()))
                .min((t1, t2) -> t1.getTaskDate().compareTo(t2.getTaskDate()));

        if (newLearningTask.isPresent()) {
            taskToComplete = newLearningTask.get();
        } else {
            // 如果没有新学任务，只在以下情况下完成复习任务：
            // 1. 今天确实有复习任务安排
            // 2. 或者复习任务已经过期（超过任务日期）
            Optional<PlanDailyTaskDO> todayReviewTask = tasksToCheck.stream()
                    .filter(task -> "review".equals(task.getTaskType()))
                    .filter(task -> task.getTaskDate().equals(today))
                    .min((t1, t2) -> t1.getSortOrder().compareTo(t2.getSortOrder()));

            if (todayReviewTask.isPresent()) {
                taskToComplete = todayReviewTask.get();
            } else {
                // 只选择已经过期的复习任务（任务日期在今天之前）
                Optional<PlanDailyTaskDO> overdueReviewTask = tasksToCheck.stream()
                        .filter(task -> "review".equals(task.getTaskType()))
                        .filter(task -> task.getTaskDate().isBefore(today))
                        .min((t1, t2) -> t1.getTaskDate().compareTo(t2.getTaskDate()));

                if (overdueReviewTask.isPresent()) {
                    taskToComplete = overdueReviewTask.get();
                }
            }
        }

        // 如果还是没有找到合适的任务，记录警告并返回
        if (taskToComplete == null) {
            log.warn("未找到合适的任务完成，用户ID: {}, 计划ID: {}, 绘本ID: {}", userId, planId, bookId);
            return;
        }

        // 检查任务是否已经完成（幂等性检查）
        if (taskToComplete.getIsCompleted()) {
            log.warn("任务已经完成，跳过重复操作: taskId={}, taskType={}, taskDate={}",
                    taskToComplete.getId(), taskToComplete.getTaskType(), taskToComplete.getTaskDate());
            return;
        }

        // 更新任务状态为已完成
        PlanDailyTaskDO updateObj = PlanDailyTaskDO.builder()
                .id(taskToComplete.getId())
                .isCompleted(true)
                .completedAt(LocalDateTime.now())
                .build();

        planDailyTaskMapper.updateById(updateObj);

        // 更新计划进度
        updatePlanProgress(planId);

        log.info("自动完成计划任务成功: taskId={}, bookId={}, taskType={}, taskDate={}",
                taskToComplete.getId(), bookId, taskToComplete.getTaskType(), taskToComplete.getTaskDate());
    }

    @Override
    public void completeTaskByBookIdAndType(Long userId, Long planId, Long bookId, String preferredTaskType) {
        // 查找该绘本的所有未完成任务
        List<PlanDailyTaskDO> allUncompletedTasks = planDailyTaskMapper.selectUncompletedTasksByBookId(planId, bookId);

        if (allUncompletedTasks.isEmpty()) {
            log.warn("未找到需要完成的计划任务，用户ID: {}, 计划ID: {}, 绘本ID: {}", userId, planId, bookId);
            return;
        }

        // 根据指定的任务类型筛选任务
        List<PlanDailyTaskDO> targetTypeTasks = allUncompletedTasks.stream()
                .filter(task -> preferredTaskType.equals(task.getTaskType()))
                .collect(Collectors.toList());

        PlanDailyTaskDO taskToComplete = null;

        if (!targetTypeTasks.isEmpty()) {
            // 如果有指定类型的任务，选择最早的
            taskToComplete = targetTypeTasks.stream()
                    .min((t1, t2) -> t1.getTaskDate().compareTo(t2.getTaskDate()))
                    .orElse(null);
        } else {
            // 如果没有指定类型的任务，回退到默认逻辑
            completeTaskByBookId(userId, planId, bookId);
            return;
        }

        // 更新任务状态为已完成
        PlanDailyTaskDO updateObj = PlanDailyTaskDO.builder()
                .id(taskToComplete.getId())
                .isCompleted(true)
                .completedAt(LocalDateTime.now())
                .build();

        planDailyTaskMapper.updateById(updateObj);

        // 更新计划进度
        updatePlanProgress(planId);

        log.info("指定类型任务完成成功: taskId={}, bookId={}, taskType={}, taskDate={}",
                taskToComplete.getId(), bookId, taskToComplete.getTaskType(), taskToComplete.getTaskDate());
    }

    @Override
    public List<PlanDailyTaskDO> getTaskStatusByBookId(Long planId, Long bookId) {
        return planDailyTaskMapper.selectTaskStatusByBookId(planId, bookId);
    }

    /**
     * 更新计划进度
     * 避免循环依赖，直接在这里实现进度更新逻辑
     */
    private void updatePlanProgress(Long planId) {
        try {
            // 获取计划信息
            ReadingPlanDO plan = readingPlanMapper.selectById(planId);
            if (plan == null) {
                log.warn("计划不存在，无法更新进度: planId={}", planId);
                return;
            }

            // 统计已完成的唯一绘本数量（每本绘本只计算一次）
            Long completedBooks = planDailyTaskMapper.countCompletedBooksByPlanId(planId);

            // 获取已完成任务的最大天数，用于更新currentDay
            Integer maxCompletedDay = planDailyTaskMapper.getMaxCompletedDayNumber(planId);
            Integer newCurrentDay = maxCompletedDay > 0 ? maxCompletedDay + 1 : 1; // 当前天数 = 最大已完成天数 + 1

            // 更新计划进度
            ReadingPlanDO updateObj = ReadingPlanDO.builder()
                    .id(planId)
                    .completedBooks(completedBooks.intValue())
                    .currentDay(newCurrentDay)
                    .build();

            // 检查是否完成
            if (completedBooks >= plan.getTotalBooks()) {
                updateObj.setStatus("completed");
                updateObj.setEndDate(LocalDate.now());
            }

            readingPlanMapper.updateById(updateObj);
            log.info("计划进度更新成功: planId={}, completedBooks={}/{}, currentDay={}",
                    planId, completedBooks, plan.getTotalBooks(), newCurrentDay);
        } catch (Exception e) {
            log.error("更新计划进度失败: planId={}", planId, e);
            // 不抛出异常，避免影响主要业务逻辑
        }
    }

}
